package pho.prowon.jpeg;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;

import pho.helper.IptcTagNameConverter;
import pho.prowon.ImageData;

/**
 * @author Peter
 *
 * This class assists the JpegWriter. It will take over the output of the APPD
 * segment. It takes the IPTC (8BIM 04 04 segment) information stored in the ImageData object and for
 * if there is anything else in the APPD segment --> bad luck... 
  */
public class IptcWriter
{
	/**
	 * The ImageData that is used as source for the Iptc information in APPD.
	 */
	private ImageData _imageData;

	/**
	 * Creates a new IptcWriter for the specified ImageData.
	 */
	public IptcWriter(ImageData imd)
	{
		_imageData = imd;
	}

	/**
	 * Writes the APPD segment starting with the length-bytes
	 * @param os the output stream the data is written to
	 * @throws IOException
	 */
	public void writeSegment(OutputStream os)
		throws IOException
	{
		// holds the length of the 8BIM 04 04 segment
		int newBimLength = 0;
		
		// this is used to find the right bytes for the 8BIM segment length
		// bytes after writing the segment 
		int bimLengthOffset = 0;
		
		// length of the data in the Iptc Tag
		int len = 0;
		
		// used to read any stuff
		byte[] buffer;

		// if 'is' is null that means that no APPD segment exists in the Input Stream, hence
		// we skip all input stream operations

		// this is the temporary container to dump the output, we have to update the segment length
		// before we put this on the real output stream os			
		ByteArrayOutputStream baos = new ByteArrayOutputStream();

		// first comes the segment size (2 Bytes)
		// set it to 0 now, we will update it when we are done
		baos.write(0x00);
		baos.write(0x00);

		// next the program name - null terminated
		// unfortunately this has to be Photoshop otherwise Photoshop is
		// refusing this whole information
		baos.write("Photoshop 3.0".getBytes());
		baos.write(0x00);

		// now we start the 8BIM segment
		// 8BIM to output
		baos.write("8BIM".getBytes());

		// set the 8BIM type to 0x04 0x04 
		// the rest is just padding
		// first 2 bytes are the segment type followed by 4 bytes padding
		// and to output
		baos.write(0x04);
		baos.write(0x04);
		baos.write(0x00);
		baos.write(0x00);
		baos.write(0x00);
		baos.write(0x00);
		
		// first comes the segment size (2 Bytes)
		// we will update this at the end
		baos.write(0x00);
		baos.write(0x00);

		// remember the position
		bimLengthOffset = baos.size() - 2;

		// now we start nailing out the image data
		Iterator iterator = _imageData.getTagNameIterator();
		while (iterator.hasNext())
		{
			String idTagName = ((String) iterator.next());

			int tagType =
				IptcTagNameConverter
					.getTagTypeFromid(idTagName)
					.intValue();

			String [] idTags;
			// default is 1 unless it's an array
			int numOfTags = 1;
			
			if (_imageData.isImageDataTagArray(idTagName))
			{
				idTags = (String []) _imageData.getImageDataTagArray(idTagName);
				numOfTags = idTags.length;
			}
			else
			{
				idTags = new String [1];
				idTags [0] = (String) _imageData.getImageDataTag(idTagName);
			}

			for (int i = 0;i < numOfTags;i++)
			{  
				if (idTags[i] != null && idTags[i].length() > 0)
				{
					// write 0x1C
					baos.write(0x1C);

					// write the tag id
					baos.write((tagType & 0xFF00) >> 8);
					baos.write((tagType & 0xFF));

					// now write the contents of the tag
					// this will also do the necessary conversions
					buffer = IptcTagNameConverter.processTag(tagType, idTags[i]);

					// write the length
					len = buffer.length;

					baos.write((len & 0xFF00) >> 8);
					baos.write((len & 0xFF));

					// and now the contents
					baos.write(buffer);
				}
			}
		}
		newBimLength = baos.size() - bimLengthOffset - 2;

		// if the length is an odd number we have to add a 0x00 otherwise
		// Photoshop refuses to write the file...
		if (newBimLength % 2 == 1)
		{
			baos.write (0x00);
			newBimLength++;
		}

		// now get all the bytes for APPD
		buffer = baos.toByteArray();

		// now put the 8BIM segment length for the IPTC 0404 segment on the right place
		if (bimLengthOffset > 0)
		{
			buffer[bimLengthOffset] = (byte) ((newBimLength & 0xFF00) >> 8);
			buffer[bimLengthOffset + 1] = (byte) (newBimLength & 0xFF);
		}
		
		// and now the APPD segment length
		buffer[0] = (byte) ((buffer.length & 0xFF00) >> 8);
		buffer[1] = (byte) (buffer.length & 0xFF);


		// and dump it to the real output stream
		os.write(buffer);
	}
}

